package io.renren.common.utils;


import java.util.Random;
import java.util.UUID;

/**
 * @author created by Singer email:313402703@qq.com
 * @time 2018/3/9
 * @description
 */
public class SnowflakeIdWorker {
    // ==============================Fields===========================================
    /** 开始时间截 (2015-01-01) */
    private final long twepoch = 1420041600000L;

    /** 机器id所占的位数 */
    private final long workerIdBits = 5L;

    /** 数据标识id所占的位数 */
    private final long datacenterIdBits = 5L;

    /** 支持的最大机器id，结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /** 支持的最大数据标识id，结果是31 */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /** 序列在id中占的位数 */
    private final long sequenceBits = 12L;

    /** 机器ID向左移12位 */
    private final long workerIdShift = sequenceBits;

    /** 数据标识id向左移17位(12+5) */
    private final long datacenterIdShift = sequenceBits + workerIdBits;

    /** 时间截向左移22位(5+5+12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /** 生成序列的掩码，这里为4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /** 工作机器ID(0~31) */
    private long workerId;

    /** 数据中心ID(0~31) */
    private long datacenterId;

    /** 毫秒内序列(0~4095) */
    private long sequence = 0L;

    /** 上次生成ID的时间截 */
    private long lastTimestamp = -1L;

    private static SnowflakeIdWorker snowflakeIdWorker;

    static {
        snowflakeIdWorker = new SnowflakeIdWorker(0, 6);
    }

    /**
     * 返回项目编号 : 时间措+4位随机字符
     */
    public static String getNextProNo(){
        return uniqueStringSequence()+ UUID.randomUUID().toString().substring(0,3);
    }

    //==============================Constructors=====================================
    /**
     * 构造函数
     * @param workerId 工作ID (0~31)
     * @param datacenterId 数据中心ID (0~31)
     */
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    // ==============================Methods==========================================
    /**
     * 获得下一个ID (该方法是线程安全的)
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        //如果是同一时间生成的，则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变，毫秒内序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的时间截
        lastTimestamp = timestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterId << datacenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
    }

    /**
     * 获得下一个用户编号
     * @return SnowflakeId
     */
    public synchronized long nextUserNo() {
        long timestamp = timeGen();

        //如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        //如果是同一时间生成的，则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变，毫秒内序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的时间截
        lastTimestamp = timestamp;

        //1571299798759
        return timestamp;
    }



    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * 返回较长的唯一ID
     * @return
     */
    public static Long uniqueSequence(){
        return snowflakeIdWorker.nextId();
    }

    /**
     * 返回较长的唯一ID
     * @return
     */
    public static String uniqueSequenceStr(){
        return EncryptUtils.MD5String(String.valueOf(snowflakeIdWorker.nextId()));
    }

    /**
     * 返回较长的唯一ID
     * @return
     */
    public static String uniqueStringSequence(){
        return String.valueOf(snowflakeIdWorker.nextId());
    }


    /**
      * @description: 返回8位用户编号
      * @auther: create by jieShao email:2609207540@qq.com
      * @date 2019/10/17
      * @param
      * @return
      */
    public static String getNextUserNo(){
        String nextUserNo = String.valueOf(snowflakeIdWorker.nextUserNo());
        Random random = new Random();
        String flag = random.nextInt(10)+""+ random.nextInt(10);
        return flag+nextUserNo.substring(7,13);
    }

    /**
     * @description: 返回32位订单号 DD+6位随机数+13时间戳  21位订单号
     * @auther: create by jieShao email:2609207540@qq.com
     * @date 2019/10/17
     * @param
     * @return
     */
    public static String getNextOrderNo(){
        String nextUserNo = String.valueOf(snowflakeIdWorker.nextUserNo());
        Random random = new Random();
        String flag ="";
        for (int i = 0; i < 6; i++) {
            flag += random.nextInt(10);
        }
        return "DD"+flag+nextUserNo;
    }

    /**
     * @description: 返回32位订单号 DD+6位随机数+13时间戳  21位订单号
     * @auther: create by jieShao email:2609207540@qq.com
     * @date 2019/10/17
     * @param prefix 订单前缀  系统订单DDS  商户订单DDM
     * @return
     */
    public static String getNextOrderNo(String prefix){
        String nextUserNo = String.valueOf(snowflakeIdWorker.nextUserNo());
        Random random = new Random();
        String flag ="";
        for (int i = 0; i < 6; i++) {
            flag += random.nextInt(10);
        }
        return prefix+flag+nextUserNo;
    }

    /**
      * @description: 生成退款订单号
      * @auther: create by jieShao email:2609207540@qq.com
      * @date 2019/11/18
      * @return
      */
    public static String getNextBackNo(){
        String nextUserNo = String.valueOf(snowflakeIdWorker.nextUserNo());
        Random random = new Random();
        String flag ="";
        for (int i = 0; i < 6; i++) {
            flag += random.nextInt(10);
        }
        return "TK"+flag+nextUserNo;
    }

    /**
      * @description: 创建32位支付订单号
      * @auther: create by jieShao email:2609207540@qq.com
      * @date 2019/11/5
      * @param
      * @return
      */
    public static String getNextPayOrderNo(){
        String nextUserNo = String.valueOf(snowflakeIdWorker.nextUserNo());
        Random random = new Random();
        String flag ="";
        for (int i = 0; i < 17; i++) {
            flag += random.nextInt(10);
        }
        return "ZF"+flag+nextUserNo;
    }

    /**
      * @description: 获取微信时间戳
      * @auther: create by jieShao email:2609207540@qq.com
      * @date 2019/11/6
      * @param
      * @return
      */
    public static String getWxPayTime() {
        long timeStampSec = System.currentTimeMillis()/1000;
        //System.out.println(timeStampSec);
        String timestamp = String.format("%010d", timeStampSec);
        return timestamp;
    }

    /**
      * 获取六位随机邀请码
      */
    public static String getNextInviteCode(){
        String flagStr = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
        //随机获取其中6个组成随机邀请码
        Random random=new Random();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<6;i++){
            int number=random.nextInt(32);
            sb.append(flagStr.charAt(number));
        }
        return sb.toString();
    }

    /**
     * @description: 获取32位随机数
     * @auther: create by jieShao email:2609207540@qq.com
     * @date 2019/11/6
     * @param
     * @return
     */
    public static String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }

    //==============================Test======================EncryptUtilsEncryptUtils=====2017/12/27 17:09:24==================
    /** 测试 */
    public static void main(String[] args) {


//        String str = getNextPayOrderNo();
//        String nonce_str = UUID.randomUUID().toString().trim().replaceAll("-", "");
        for (int i = 0; i < 10; i++) {
            System.out.println(SnowflakeIdWorker.uniqueSequenceStr());
        }
        System.out.println(SnowflakeIdWorker.uniqueSequenceStr());
        /*String s = "shadhsahdkas";
        boolean shadh = s.startsWith("shadh");*/
//        System.out.println(String.valueOf(nextUserNo()));
        /*Date date = DateTimeUtil.strToDate("20141030133525","yyyyMMddHHmmss");
        System.out.println(date);*/
        /*Map<String, String> city = new HashMap<>();
        city.put("hehe","123432");
        city.put("hahahah","nimei");
        String jsonString = JSON.toJSONString(city);
        System.out.println(jsonString);*/

//        Map<String,Object> newMap = JSON.parseObject(jsonString, Map.class);

        //1571298372406
        //1571298405541
        //1571299798759
        /*for (int i = 0; i < 100; i++) {
            System.out.println(getNextUserNo());
        }
*/

    }

}
